import { sounds } from '../const';
import Emitter from './emitter';

class Loader extends Emitter {
    constructor(url) {
        super();
        this._url = url;
    }

    get url() {
        return this._url;
    }

    _load() {
        return new Promise(resolve => {
            this.emit('start');
            let xhr = new XMLHttpRequest();
            xhr.open('GET', this.url);
            xhr.responseType = 'arraybuffer';
            xhr.addEventListener('readystatechange', () => {
                if (xhr.readyState === 4 && xhr.status === 200) {
                    this.emit('loaded');
                    resolve(xhr.response);
                }
            });
            xhr.addEventListener('progress', ({ loaded, total }) => this.emit('progress', { loaded, total }));
            xhr.send();
        });
    }

    load() {
        return this._load();
    }
}

class Sounder {
    constructor(buffer) {
        this._buffer = buffer;
        this._audio = null;
        this._stop = false;
    }

    play(loop = false) {
        return new Promise(resolve => {
            let context = new (window.AudioContext || window.webkitAudioContext)();
            context.decodeAudioData(this._buffer, decodeBuffer => {
                let source = this._audio = context.createBufferSource();
                source.buffer = decodeBuffer;
                source.loop = loop;
                source.connect(context.destination);
                source.addEventListener('ended', () => resolve());
                source.addEventListener('stop', () => resolve());
                if (!this._stop) {
                    source.start(0);
                } else {
                    resolve();
                }
            });
        });
    }

    stop() {
        if (this._audio) {
            this._audio.stop();
        }
        this._stop = true;
    }
}

class SoundChannel {
    constructor() {
        this._list = [];
        this._run = false;
        this._current = null;
    }

    play(key) {
        return new Promise(resolve => {
            this._list.push({ resolve, key });
            this._trigger();
        });
    }

    playEx(key) {
        if (this._current) {
            this._current.stop();
        }
        this._list = [];
        return this.play(key);
    }

    stop() {
        if (this._current) {
            this._current.stop();
        }
    }

    _trigger() {
        if (!this._run) {
            if (this._list.length > 0) {
                this._run = true;
                let { resolve, key } = this._list.shift();
                new Loader(sounds[key]).load().then(buffer => {
                    let sounder = this._current = new Sounder(buffer);
                    return sounder.play();
                }).then(a => {
                    this._run = false;
                    resolve(a);
                    this._trigger();
                }, e => {
                    this._run = false;
                    console.log(e);
                    resolve(e);
                    this._trigger();
                });
            } else {
                this._current = null;
                this._run = false;
            }
        }
    }
}

export default SoundChannel;